Understanding C# Anonymous Methods

As you have seen, when a caller wishes to listen to incoming events, it must define a custom method in a class (or structure) that matches the signature of the associated delegate, for example:

class Program
{
    static void Main(string[] args)
    {    
        SomeType t = new SomeType();

        // Assume "SomeDelegate" can point to methods taking no
        // args and returning void.
        t.SomeEvent += new SomeDelegate(MyEventHandler);
    }

    // Typically only called by the SomeDelegate object.
    public static void MyEventHandler()
    {
        // Do something when event is fired.
    }
}

When you think about it, however, methods such as MyEventHandler() are seldom intended to be called by any part of the program other than the invoking delegate. As far as productivity is concerned, it is a bit of a bother (though in no way a showstopper) to manually define a separate method to be called by the delegate object.

To address this point, it is possible to associate an event directly to a block of code statements at the time of event registration. Formally, such code is termed an anonymous method. To illustrate the syntax, check out the following Main() method, which handles the events sent from the Car class using anonymous methods, rather than specifically named event handlers:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("***** Anonymous Methods *****\n");
        Car c1 = new Car("SlugBug", 100, 10);

        // Register event handlers as anonymous methods.
        c1.AboutToBlow += delegate
        {
            Console.WriteLine("Eek! Going too fast!");
        };

        c1.AboutToBlow += delegate(object sender, CarEventArgs e)
        {
            Console.WriteLine("Message from Car: {0}", e.msg);
        };

        c1.Exploded += delegate(object sender, CarEventArgs e)
        {
            Console.WriteLine("Fatal Message from Car: {0}", e.msg);
        };

        // This will eventually trigger the events.
        for (int i = 0; i < 6; i++)
            c1.Accelerate(20);
    
        Console.ReadLine();
    }
}

Note The final curly bracket of an anonymous method must be terminated by a semicolon. If you fail to do so, you are issued a compilation error.

Again, notice that the Program type no longer defines specific static event handlers such as CarAboutToBlow() or CarExploded(). Rather, the unnamed (aka anonymous) methods are defined inline at the time the caller is handling the event using the += syntax. The basic syntax of an anonymous method matches the following pseudo-code:

class Program
{
    static void Main(string[] args)
    {
        SomeType t = new SomeType();
        t.SomeEvent += delegate (optionallySpecifiedDelegateArgs)
        { /* statements */ };
    }
}

When handling the first AboutToBlow event within the previous Main() method, notice that you are not specifying the arguments passed from the delegate:

c1.AboutToBlow += delegate
{
    Console.WriteLine("Eek! Going too fast!");
};

Strictly speaking, you are not required to receive the incoming arguments sent by a specific event. However, if you wish to make use of the possible incoming arguments, you will need to specify the parameters prototyped by the delegate type (as shown in the second handling of the AboutToBlow and Exploded events). For example:

c1.AboutToBlow += delegate(object sender, CarEventArgs e)
{
    Console.WriteLine("Critical Message from Car: {0}", e.msg);
};

Accessing Local Variables

Anonymous methods are interesting in that they are able to access the local variables of the method that defines them. Formally speaking, such variables are termed outer variables of the anonymous method. A few important points about the interaction between an anonymous method scope and the scope of the defining method should be mentioned:

Assume our Main() method defined a local integer named aboutToBlowCounter. Within the anonymous methods that handle the AboutToBlow event, we will increment this counter by 1 and print out the tally before Main() completes:

static void Main(string[] args)
{
    Console.WriteLine("***** Anonymous Methods *****\n");
    int aboutToBlowCounter = 0;

    // Make a car as usual.
    Car c1 = new Car("SlugBug", 100, 10);

    // Register event handlers as anonymous methods.
    c1.AboutToBlow += delegate
    {
        aboutToBlowCounter++;
        Console.WriteLine("Eek! Going too fast!");
    };

    c1.AboutToBlow += (object sender, CarEventArgs e)
    {
        aboutToBlowCounter++;
        Console.WriteLine("Critical Message from Car: {0}", e.msg);
    };
...
    Console.WriteLine("AboutToBlow event was fired {0} times.",
        aboutToBlowCounter);
    Console.ReadLine();
}

Once you run this updated Main() method, you will find the final Console.WriteLine() reports the AboutToBlow event was fired twice.

Source Code The AnonymousMethods project is located under the Chapter 11 subdirectory.